package com.tianji.learning.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.tianji.common.utils.CollUtils;
import com.tianji.common.utils.DateUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.learning.constants.RedisConstants;
import com.tianji.learning.domain.po.PointsRecord;
import com.tianji.learning.domain.vo.PointsStatisticsVO;
import com.tianji.learning.domain.vo.SignResultVO;
import com.tianji.learning.enums.PointsRecordType;
import com.tianji.learning.mapper.PointsRecordMapper;
import com.tianji.learning.service.IPointsRecordService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jdk.jfr.SettingControl;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.connection.BitFieldSubCommands;
import org.springframework.data.redis.core.BoundListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.lang.module.Configuration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
 * <p>
 * 学习积分记录，每个月底清零 服务实现类
 * </p>
 *
 * @author 积分榜
 * @since 2023-04-17
 */
@Service
@RequiredArgsConstructor
public class PointsRecordServiceImpl extends ServiceImpl<PointsRecordMapper, PointsRecord> implements IPointsRecordService {

    private final RedisTemplate redisTemplate;

    /**
     * 签到
     * @return
     */
    @Override
    public SignResultVO singresult() {
        //签到
        //1获取登录用户
        Long userId = UserContext.getUser();
        //2获取当天日期
        LocalDate now = LocalDate.now();
        //3拼接key
        String key = RedisConstants.sign_record_key_prefix
                + userId
                + now.format(DateUtils.SIGN_DATE_SUFFIX_FORMATTER);
        //计算offset
        int offset =  now.getDayOfMonth() - 1;
        //缓存登录信息
        //保存签到信息
        redisTemplate.opsForValue().setBit(key,offset,true);
        //计算连续签到日期
        int sign =  countSignDays(key,now.getDayOfMonth());
        SignResultVO resultVO = new SignResultVO();
        resultVO.setSignDays(sign);
        resultVO.setRewardPoints(0);
        return resultVO;
    }

    /**
     * 新增积分
     * @param userId
     * @param points
     * @param qa
     */
    @Override
    public void addPointsRecord(Long userId, int points, PointsRecordType qa) {
//        LocalDateTime now = LocalDateTime.now();
//        //判断当前方式有没有积分上限
//        int maxPoints = qa.getMaxPoints();
//        int realPoints = i;
//        if (maxPoints > 0){
//            //有，则需要判断是否超过上限
//            LocalDateTime dayStartTime = DateUtils.getDayStartTime(now);
//            LocalDateTime dayEndTime = DateUtils.getDayEndTime(now);
//            //查询今日已得积分，封装为一个函数
//             Integer cuesser = queryUserPointByTypeAndDate(userId,qa,dayStartTime,dayEndTime);
//             if (cuesser==null){
//                 cuesser=0;
//             }
//            //判断是否超过上限
//        if (cuesser >= maxPoints){
//            //超过，直接结束
//            return;
//        }
//            //没超过，保存积分记录
//            if (cuesser + i > maxPoints){
//                realPoints =  maxPoints - cuesser;
//            }
//
//        }
//        //没有，直接保存积分
//        PointsRecord p = new PointsRecord();
//        p.setPoints(realPoints);
//        p.setUserId(userId);
//        p.setType(qa);
//        save(p);
//
//        //添加积分到积分榜单,并缓存到Redis中
//        String key = RedisConstants.POINTS_BOARD_KEY_PREFIX + now.format(DateUtils.SIGN_DATE_SUFFIX_FORMATTER);
//        redisTemplate.opsForZSet().incrementScore(key,p.getPoints(),realPoints);

        //1.判断是否有积分上限
        int maxPoints = qa.getMaxPoints();
        int realPoints = points;
        if (maxPoints > 0) {
            LocalDateTime end = DateUtils.getDayEndTime(LocalDateTime.now());
            LocalDateTime begin = DateUtils.getDayStartTime(LocalDateTime.now());
            //1.2 是，查询今日以获得积分
            Integer total = queryPointsByTypeAndDate(userId, qa, begin, end);
            if (total == null) {
                total=0;
            }
            //2. 是否超过每日上限

            if (total >= maxPoints) {
                //2.1 查询以获得积分是否超过上限
                return;
            }

            //2.2 查询添加本次积分后 ，是否超过上限
            if (total + points > maxPoints) {
                realPoints = maxPoints - total;
            }
        }
        //3.保存积分记录
        PointsRecord record = new PointsRecord();
        record.setPoints(realPoints);
        record.setCreateTime(LocalDateTime.now());
        record.setId(userId);
        record.setType(qa);
        record.setUserId(userId);
        save(record);
        ////

        // 4.更新总积分到Redis
        String key = RedisConstants.POINTS_BOARD_KEY_PREFIX + LocalDateTime.now().format(DateUtils.POINTS_BOARD_SUFFIX_FORMATTER);
        redisTemplate.opsForZSet().incrementScore(key, userId.toString(), realPoints);


    }

    private int queryUserPointByTypeAndDate(Long userId, PointsRecordType qa, LocalDateTime dayStartTime, LocalDateTime dayEndTime) {
        //查询条件
        QueryWrapper<PointsRecord> wrapper = new QueryWrapper<>();
        wrapper.lambda()
                .eq(PointsRecord::getUserId,userId)
                .eq(qa != null, PointsRecord::getType,qa)
                .between(dayStartTime != null || dayEndTime != null, PointsRecord::getCreateTime,dayStartTime,dayEndTime);
        //调用Mapper，查询结果
        Integer print = getBaseMapper().queryUserPointAndDate(wrapper);
        //判断返回值
        return print == null ? 0 : print;
    }

    /**
     * 查询今日积分情况
     * @return
     */
    @Override
    public List<PointsStatisticsVO> points() {
        //获取用户id
        Long userId = UserContext.getUser();
        //获取当前时间
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime dayStartTime = DateUtils.getDayStartTime(now);
        LocalDateTime dayEndTime = DateUtils.getDayEndTime(now);
        //构建查询条件
        QueryWrapper<PointsRecord> objectQueryWrapper = new QueryWrapper<>();
        objectQueryWrapper.lambda()
                .eq(PointsRecord::getUserId,userId)
                .between(PointsRecord::getCreateTime,dayEndTime,dayStartTime);

        //查询数据库
        List<PointsRecord> list = getBaseMapper().listRecord(objectQueryWrapper);
        if (CollUtils.isEmpty(list)){
            return CollUtils.emptyList();
        }
        List<PointsStatisticsVO> vos = new ArrayList<>(list.size());
        //转VO为PO
        for (PointsRecord p : list) {
            PointsStatisticsVO vo = new PointsStatisticsVO();
            vo.setPoints(p.getPoints());
            vo.setType(p.getType().getDesc());
            vo.setMaxPoints(p.getType().getMaxPoints());
            vos.add(vo);
        }
        return vos;
    }

    private int countSignDays(String key, int dayOfMonth) {
        //获取本月从第一天开始的次数
        List<Long> list = redisTemplate.opsForValue()
                .bitField(key, BitFieldSubCommands.create().get(BitFieldSubCommands.BitFieldType.unsigned(dayOfMonth)).valueAt(0));
        if (CollUtils.isEmpty(list)){
            return 0;
        }
        int aLong = list.get(0).intValue();
        //定义一个计数器
         int count = 0;
        //循环，与1做比较，得到最后一个bit，判断是否为0，如果是1则继续
        while ((aLong & 1 ) == 1){
            //计数器+1
            count++;
            //把数字右移一位，最后一位被舍弃，倒数第二位成为最后一位
            aLong >>>= 1;
        }
        //将最后的计算结果进行返回
       return  count;
    }

    private Integer queryPointsByTypeAndDate(Long userId, PointsRecordType qa, LocalDateTime begin, LocalDateTime end) {

        QueryWrapper<PointsRecord> wrapper = new QueryWrapper<>();

        wrapper.lambda().eq(PointsRecord::getUserId, userId)
                .eq(qa != null, PointsRecord::getType, qa)
                .between(begin != null && end != null, PointsRecord::getCreateTime, begin, end);

        return getBaseMapper().queryPointsByDate2(wrapper);
    }
}
